package semaphore

import (
	"errors"
	"sync"
	"time"
)

/**
并发访问控制
*/

var (
	ErrInvalidTimeout = errors.New("invalid timeout")
	ErrTimeout        = errors.New("timeout")
	ErrMismatch       = errors.New("release called without a successful Acquire")
)

var instance *semaphore

/**
limit:最大并发访问数量
*/
func NewSemahore(limit uint) *semaphore {
	sync.Once{}.Do(func() {
		instance = &semaphore{
			seq:     0,
			state:   uint64(limit) << 32,
			pending: make(map[uint64]*wrapper),
			userIds: make(map[int64]bool, limit),
		}
	})
	return instance
}

type wrapper struct {
	userId int64
	doneC  chan struct{}
}

type semaphore struct {
	l       sync.Mutex
	seq     uint64
	state   uint64
	pending map[uint64]*wrapper
	userIds map[int64]bool
}

func (s *semaphore) Acquire(userId int64, timeout time.Duration) error {
	if timeout <= 0 {
		return ErrInvalidTimeout
	}
	s.l.Lock()
	limit := s.state >> 32
	count := s.state & 0xFFFFFFFF
	if count < limit && !s.userIds[userId] {
		s.userIds[userId] = true
		s.state++
		s.l.Unlock()
		return nil
	}

	s.seq++
	seq := s.seq
	td := &wrapper{
		userId: userId,
		doneC:  make(chan struct{}),
	}
	s.pending[seq] = td
	s.l.Unlock()

	select {
	case <-td.doneC:
		return nil
	case <-time.After(timeout):
		select {
		case <-td.doneC: // double check
			return nil
		default:
			s.l.Lock()
			defer s.l.Unlock()
			delete(s.pending, seq)
		}
		return ErrTimeout
	}
}

func (s *semaphore) Release(userId int64) error {
	s.l.Lock()
	defer s.l.Unlock()

	if !s.userIds[userId] {
		return ErrMismatch
	}

	delete(s.userIds, userId)
	s.state--

	for seq, td := range s.pending {
		if s.userIds[td.userId] {
			continue
		}
		s.state++
		s.userIds[td.userId] = true
		close(td.doneC)
		delete(s.pending, seq)
		return nil
	}
	return nil
}
